
_G['code'] = ''
_G['techniques'] = {}

function glsl(source)
	local lineNumber = debug.getinfo(2).currentline 
	_G['code'] = _G['code'] .. '\n#line ' .. lineNumber .. '\n' .. source
end

function pack(index, head, ...)
    if head == nil then
        return {}
    end
    local array = pack(index + 1, ...)
    array[index] = head
    return array
end

function technique(name)
    local lineNumber = debug.getinfo(2).currentline

    return function(...)
		local techniques = _G['techniques']
		techniques[#techniques + 1] = {
            nodeType = 'technique',
            line = lineNumber,
            name = name,
            passes = pack(0, ...)
        }
	end
end

function pass(name)
    local lineNumber = debug.getinfo(2).currentline

    return function(states)
        return {
            line = lineNumber,
            nodeType = "pass",
            name = name,
            states = states
        }
	end
end

function compile(profile, program)
    local lineNumber = debug.getinfo(2).currentline
    local source = '';
    if profile == 'vp_gles' then
        source = [[
            #version 100
            #define write(dest, source) dest = source;
            vec4 glfx_dummyFragColor;
            #define gl_FragColor glfx_dummyFragColor
        ]]
    elseif profile == 'fp_gles' then
        source = [[
            #version 100
            #define write(dest, source)
            vec4 glfx_dummyPosition;
            #define gl_Position glfx_dummyPosition
            #define attribute
        ]]
    else
        error("Unknown glsl profile: " .. profile)    
    end

    source = source .. _G['code'] .. '\nvoid main() { ' .. program .. '(); }\n'
    
    return source;
    --return { line = lineNumber, nodeType = "compile", profile = profile, program = program }
end

function lockglobalenvironment()
    local function _n(t, i, k) 
        --rawset(t, i, i)
        --error("writing to new global variables is disabled.", 2);
    end

    local function _i(t, i)
        return rawget(t, i) or i
        --error("reading to new global variables is disabled.", 2);
    end

    -- work with the global variable table ('_G')
    local mt = { __index = _i };
    setmetatable(_G, mt);
end

-- this reverts the global variable lock.
function unlockglobalenvironment()
    local mt = getmetatable(_G);
    mt.__index = nil;
    mt.__newindex = nil;
end

--------------------------------------------------------------------------------
-- Validation
--------------------------------------------------------------------------------

function validateNodeType(name, node, nodeType)
    assert(type(node) == 'table', 'Expected ' .. (name or '?') .. ' to be node of type ' .. nodeType .. ', found ' .. type(node))
    assert(node.nodeType == nodeType, name .. ' on line ' .. (node.line or '?') .. ' is not a valid ' .. nodeType .. ' (type = ' .. (node.nodeType or '<nil>') .. ')')
end

function validatePass(techniqueName, index, pass)
    validateNodeType(techniqueName .. ' pass ' .. index, pass, 'pass')
end

function validateTechnique(name, technique)
    validateNodeType(name, technique, 'technique')
    for index, pass in pairs(technique.passes) do
        validatePass(technique.name, index, pass)
    end
end

function validate()
    unlockglobalenvironment()
    for name, technique in pairs(_G['techniques']) do
        validateTechnique(name, technique)
    end
    lockglobalenvironment()
end

lockglobalenvironment()

